package testing2

import (
	"fmt"
	"runtime"
	"sync"
)

/*
使用协程（goroutines）+ channel 提高程序的性能测试
*/
func main() {

	// 使用一个CPU进行计算
	sum := Sum11()
	fmt.Println(sum)

	// 多核并发（花费时间比单核cpu还长。这是由于CPU缓存导致的，对一个切​​片的并发更新都会导致同一缓存行下的切片都要重复大量的缓存同步）
	sum2 := Sum112()
	fmt.Println(sum2)

	// 使用通道和协程并发计算（用channel传递数据，不在需要自己去加锁维护一个全局的阻塞队列）
	sum3 := Sum113()
	fmt.Println(sum3)
}

const (
	// 100亿
	LIMIT = 1e10
)

func Sum11() int {

	sum := 0
	for i := 0; i <= LIMIT; i++ {

		sum += i
	}
	return sum
}

func Sum112() int {

	/*
		runtime.GOMAXPROCS(逻辑Cpu数量): 获取可用的硬件线程总数n。逻辑Cpu数量有如下几个数值：
		● <1：不修改任何数值；
		● =1：单核心执行；
		● >1：多核并发执行。一般情况下，可以使用runtime.Num CPU()查询CPU数量
	*/
	n := runtime.GOMAXPROCS(0) // 获取可用的硬件线程总数n
	sums := make([]int, n)     // 建立一个长度为整数n个的切片sums来保存计算结果
	wg := sync.WaitGroup{}     // 使用sync的WaitGroup功能来管理goroutines
	for i := 0; i < n; i++ {
		wg.Add(1) // Add方法用来增加或者减少goroutine的数量
		go func(i int) {
			start := (LIMIT / n) * i
			end := start + (LIMIT / n)
			for j := start; j < end; j++ {
				sums[i] += j
			}
			wg.Done() // Done方法其实上等于Add（-1）
		}(i) // 通过函数参数传入i，防止闭包错误的引用同一个变量
	}

	wg.Wait() // Wait方法可以用来阻塞并检测 goroutine的完成
	sum2 := 0
	for _, i := range sums {
		sum2 += i
	}

	return sum2
}

func Sum113() int {

	n := runtime.GOMAXPROCS(runtime.NumCPU() - 1)
	res := make(chan int, n) // 使用缓冲通道，大小设置为n
	for i := 0; i < n; i++ {

		// 开启n个协程并发执行，使用只写channel的方式来传输数据
		go func(i int, r chan<- int) {
			sum := 0
			start := (LIMIT / n) * i
			end := start + (LIMIT / n)
			for j := start; j < end; j++ {
				sum += j
			}
			r <- sum
		}(i, res) // 通过函数参数传入i，防止闭包错误的引用同一个变量
	}

	sum3 := 0
	for i := 0; i < n; i++ {
		i2 := <-res
		sum3 += i2
	}

	return sum3
}
